"""
------------------------------------------
@File       : employee_manager_system.py
@Author     : maixiaochai
@Email      : maixiaochai@outlook.com
@CreatedOn  : 2022/3/24 10:05
------------------------------------------
员工管理系统，改进后的版本
干货很多
参考：
    https://mp.weixin.qq.com/s/HMO1WSwO7JC9ggR5XYzXfQ
"""

from dataclasses import dataclass
from typing import List
from enum import Enum, auto
from abc import ABC, abstractmethod

# The fixed number of vacation days that can be paid out.
FIXED_VACATION_DAYS_PAYOUT = 5


class Role(Enum):
    """Employee roles"""
    PRESIDENT = auto()
    VICEPRESIDENT = auto()
    MANAGER = auto()
    LEAD = auto()
    WORKER = auto()
    INTERN = auto()


@dataclass
class Employee(ABC):
    """Basic representation of an employee at the company"""

    name: str
    role: Role
    vacation_days: int = 25

    def take_a_holiday(self, payout: bool) -> None:
        """Let the employee take a single holiday."""
        if self.vacation_days < 1:
            raise ValueError(
                "You don't have any holidays left. Now back to work, you!"
            )
        self.vacation_days -= 1
        print("Have fun on your holiday. Don't forget to check out your emails!")

    def payout_a_holiday(self) -> None:
        """Let the employee get paid for unused holidays."""
        if self.vacation_days < FIXED_VACATION_DAYS_PAYOUT:
            raise VacationDaysShortageError(
                FIXED_VACATION_DAYS_PAYOUT,
                self.vacation_days,
                f"You don't have enough holidays left over for a payout"
                f"Remaining holidays: {self.vacation_days}")

        self.vacation_days -= FIXED_VACATION_DAYS_PAYOUT
        print(f"Paying out a holiday. Holidays left: {self.vacation_days}")

    @abstractmethod
    def pay(self):
        pass


@dataclass
class HourlyEmployee(Employee):
    """ Employee that's paid based on number of worked hours."""

    hourly_rate_dollars: float = 50
    hours_worked: int = 10

    def pay(self):
        print(f"Paying employee {self.name} a hourly rate of ${self.hourly_rate_dollars} for {self.hours_worked} hours")


@dataclass
class SalariedEmployee(Employee):
    """Employee that's paid based on a fixed monthly salary."""

    monthly_salary: float = 5000

    def pay(self):
        print(f"Paying employee {self.name} a monthly salary of ${self.monthly_salary}")


class Company:
    """Represents a company with employees."""

    def __init__(self) -> None:
        self.employees: List[Employee] = []

    def add_employee(self, employee: Employee) -> None:
        self.employees.append(employee)

    def find_employees(self, role: Role) -> List[Employee]:
        return [employee for employee in self.employees if employee.role is role]


class VacationDaysShortageError(Exception):
    """Custom error that is raised when not enough vacation days available."""

    def __int__(self, requested_days: int, remaining_days: int, message: str) -> None:
        self.requested_days = requested_days
        self.remaining_days = remaining_days
        self.message = message
        super().__int__(message)


def main() -> None:
    company = Company()
    company.add_employee(SalariedEmployee(name="Louis", role=Role.MANAGER))
    company.add_employee(HourlyEmployee(name="Brenda", role=Role.VICEPRESIDENT))
    company.add_employee(HourlyEmployee(name="Tim", role=Role.INTERN))
    print(company.find_employees(Role.VICEPRESIDENT))
    print(company.find_employees(Role.MANAGER))
    print(company.find_employees(Role.INTERN))

    company.employees[0].pay()
    company.employees[0].take_a_holiday(False)


class DemoE(Employee):
    def pay(self):
        print("Demo E")


def demo():
    role = Role
    print(role.MANAGER)
    print(role.MANAGER.value)
    d = DemoE('demo', Role.MANAGER)
    print(d)


if __name__ == '__main__':
    main()
